package com.xwaiy.xwaiyrpc.fault.tolerant;

import com.xwaiy.xwaiyrpc.config.RpcConfig;
import com.xwaiy.xwaiyrpc.exception.RpcException;
import com.xwaiy.xwaiyrpc.exception.enums.ErrorCode;
import com.xwaiy.xwaiyrpc.fault.retry.RetryStrategy;
import com.xwaiy.xwaiyrpc.fault.retry.RetryStrategyFactory;
import com.xwaiy.xwaiyrpc.loadbalancer.LoadBalancer;
import com.xwaiy.xwaiyrpc.loadbalancer.LoadBalancerFactory;
import com.xwaiy.xwaiyrpc.model.RpcRequest;
import com.xwaiy.xwaiyrpc.model.RpcResponse;
import com.xwaiy.xwaiyrpc.RpcApplication;
import com.xwaiy.xwaiyrpc.model.ServiceMetaInfo;
import com.xwaiy.xwaiyrpc.server.tcp.VertxTcpClient;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 故障转移 - 自动切换到备用服务实例
 * @Author Xwaiy
 * @Date 2025/9/3 17:56
 **/
@Slf4j
public class FailOverTolerantStrategy implements TolerantStrategy {

    // 最大重试次数
    private static final int MAX_RETRY_COUNT = 3;

    @Override
    public RpcResponse doTolerant(Map<String, Object> context, Exception e) {
        RpcRequest request = (RpcRequest) context.get("request");
        ServiceMetaInfo originalService = (ServiceMetaInfo) context.get("selectedService");
        List<ServiceMetaInfo> serviceList = (List<ServiceMetaInfo>) context.get("serviceList");

        if (request == null) {
            throw new RpcException(ErrorCode.CONSUMER_INVOCATION_FAILED, "服务调用失败，请求参数为空", e);
        }

        if (serviceList == null || serviceList.size() <= 1) {
            throw new RpcException(ErrorCode.CONSUMER_SERVICE_UNAVAILABLE, "服务调用失败且无法故障转移，没有备用服务实例", e);
        }

        // 移除失败的服务实例
        List<ServiceMetaInfo> availableServices = serviceList.stream()
                .filter(service -> originalService == null || !service.equals(originalService))
                .collect(Collectors.toList());

        if (availableServices.isEmpty()) {
            throw new RpcException(ErrorCode.CONSUMER_SERVICE_UNAVAILABLE, "所有服务实例都不可用", e);
        }

        Exception lastException = e;

        // 尝试故障转移
        for (int i = 0; i < Math.min(MAX_RETRY_COUNT, availableServices.size()); i++) {
            try {
                // 使用负载均衡选择新的服务实例
                RpcConfig rpcConfig = RpcApplication.getRpcConfig();
                LoadBalancer loadBalancer = LoadBalancerFactory.getInstance(rpcConfig.getLoadBalancer());
                Map<String, Object> params = new HashMap<>();
                params.put("method", request.getMethodName());
                ServiceMetaInfo newService = loadBalancer.select(params, availableServices);

                log.info("尝试故障转移到服务实例: {}:{}",
                        newService.getServiceHost(), newService.getServicePort());

                // 使用重试机制进行请求
                RetryStrategy retryStrategy = RetryStrategyFactory.getInstance(rpcConfig.getRetryStrategy());
                RpcResponse response = retryStrategy.doRetry(() ->
                        VertxTcpClient.doRequest(request, newService)
                );

                if (response != null) {
                    log.info("故障转移成功到服务实例: {}:{}",
                            newService.getServiceHost(), newService.getServicePort());
                    return response;
                }
            } catch (Exception ex) {
                lastException = ex;
                log.warn("故障转移尝试 {} 失败: {}", i + 1, ex.getMessage());
            }
        }

        throw new RpcException(ErrorCode.CONSUMER_INVOCATION_FAILED, "所有故障转移尝试都失败", lastException);
    }
}